#include <cassert>
#include <iostream>
#include "curling/core.hpp"
#include "curling/gui.hpp"
#include "curling/renderer.hpp"
#include "curling/game.hpp"

namespace curling {

/**
 * Listens to events from the GUI and passes them on to the core.
 */
class core_gui_listener : public gui_listener
{
public:

  core_gui_listener( core* _this_ )
  : _this(_this_) {}
  
  void quit()
  {
    _this->quit_event();
  }
  
  void play()
  {
    _this->play_event();
  }
  
  void exit()
  {
    _this->exit_event();
  }
  
  void pause()
  {
    _this->pause_event();
  }

  void options()
  {
    _this->options_event();
  }
  
  void resume()
  {
    _this->resume_event();
  }

  void player_aim( vector2d target )
  {
  }
  
  void player_deliver( double direction, double power, double spin )
  {
    _this->deliver_event(direction, power, spin);
  }
  
  void player_begin_sweep()
  {
    _this->begin_sweep_event();
  }
  
  void player_end_sweep()
  {
    _this->end_sweep_event();
  }

  void fade_complete()
  {
    _this->fade_complete_event();
  }
  
  void play_title_music() {}
  void stop_title_music() {}
  void start_arena_sound() {}
  void stop_arena_sound() {}
  void focus_item() {}
  void select_item() {}
  void game_attached( boost::shared_ptr<game> ) {};
  void game_detached( boost::shared_ptr<game> ) {};
  
  void camera_animation_done() {}
  void message_display_done() {}
  
private:
  
  core* _this;
};

core::core( int display_width, int display_height )
: _current_state(state_initial),
  _requests_quit(false),
  wait_time(0.0)
{
  double base_unit = 10.0;//base unit in pixels
  double gui_width = display_width / base_unit;
  double gui_height = display_height / base_unit;
  
  _gui = boost::shared_ptr<gui>(new gui(vector2d(gui_width, gui_height), base_unit));
  _gui->add_listener(boost::shared_ptr<core_gui_listener>(new core_gui_listener(this)));
  
  _renderer = boost::shared_ptr<renderer>(new renderer(_gui));
  
  _gui->show_credits_screen();
  _gui->play_title_music();
  switch_state(state_from_initial_to_credits);
  
  _gui->fade_out(true);
  _gui->fade_in();
}

core::~core()
{
}

void core::key_down( double t, int key, char character )
{
  _gui->key_down(t, key, character);
}

void core::key_up( double t, int key, char character )
{
  _gui->key_up(t, key, character);
}

void core::render( double t, double dt )
{
  // Update wait timer.
  if (wait_time > 0.0)
  {
    wait_time -= dt;
    if (wait_time <= 0.0)
    {
      wait_over_event();
    }
  }
  
  /*
   * This is just a quick hack to get a game going. It needs to be changed later.
   */
  if (_game and (current_state() == state_ingame))
  {
    if (wait_time <= 0.0)
    {
      if (_game->beginning_game())
      {
        _game->begin_game();
        _gui->game_character_controller()->animate_entrance();
      }
      else if (_game->beginning_end())
      {
        _game->begin_end();
        wait_time += 2.0;
      }
      else if (_game->player_beginning_shot())
      {
        _game->player_begin_shot();
        _gui->game_character_controller()->animate_begin_shot();
        if (_gui->current_camera() == _gui->arena_camera())
        {
          _gui->animate_to_camera(_gui->stone_camera(), 5.0);
        }
        else
        {
          _gui->animate_to_camera(_gui->stone_camera(), 1.0);
        }
        //_gui->display_message("Player X for the win", 2.0);
        _gui->show_powermeter();
        _gui->setup_shot(0.75, 0.0, 0.0, 1.0, 1.0, 1.0);
      }
      else if (_game->player_ending_shot())
      {
        _game->player_end_shot();
        wait_time += 2.0;
        _gui->hide_powermeter();
        _gui->finish_shot();
      }
      else if (_game->ending_end())
      {
        _game->end_end();
        wait_time += 4.0;
      }
      else if (_game->ending_game())
      {
        _game->end_game();
        // show winner screen
        if (_game->player_one_score() > _game->player_two_score())
        {
          _gui->show_player_one_winner_screen();
        }
        if (_game->player_one_score() < _game->player_two_score())
        {
          _gui->show_player_two_winner_screen();
        }
        if (_game->player_one_score() == _game->player_two_score())
        {
          _gui->show_player_one_winner_screen();
          _gui->show_player_two_winner_screen();
        }
        wait_time += 10.0;
      }
    }
    _game->simulate(dt);
  }
  
  _gui->animate(t, dt);
  _renderer->render(t, dt);
}

bool core::requests_quit() const
{
  return _requests_quit;
}

void core::request_quit()
{
  _requests_quit = true;
}

void core::quit_event()
{
  switch (current_state())
  {
  //case state_main_menu:
  //  _gui->fade_out();
  //  switch_state(state_quit);
  //  break;
    
  default:
    assert(false);
    break;
  }
}

void core::play_event()
{
  switch (current_state())
  {
  //case state_main_menu:
  //  _gui->flash_out();
  //switch_state(state_from_main_menu_to_start_menu);
  //  break;
    
  case state_start_menu:
    _gui->flash_out();
    switch_state(state_from_start_menu_to_ingame);
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::pause_event()
{
  switch (current_state())
  {
  case state_ingame:
    _gui->flash_out();
    switch_state(state_from_ingame_to_game_menu);
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::options_event() {

	switch (current_state()) {
        case state_start_menu:
          _gui->hide_start_menu();
          _gui->show_options_menu();
          switch_state(state_options_menu);
          break;
          
		//case state_main_menu:
		//	_gui->hide_start_menu();
		//	_gui->show_options_menu();
		//	switch_state(state_options_menu);
		//	break;

			//Not nice.. Changed this because the current_state() had often wrong state here. Works sometimes. TODO!
		//default: //case state_options_menu:
		//	_gui->hide_options_menu();
		//	_gui->show_start_menu();
		//	switch_state(state_main_menu);
		//	break;

		default:
			assert(false);
			break;
	}
}

void core::resume_event()
{
  switch (current_state())
  {
  case state_game_menu:
    _gui->flash_out();
    switch_state(state_from_game_menu_to_ingame);
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::deliver_event( double direction, double power, double spin )
{
  switch (current_state())
  {
  case state_ingame:
    _game->player_deliver(direction, power, spin);
    _gui->game_character_controller()->animate_shot();
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::begin_sweep_event()
{
  switch (current_state())
  {
  case state_ingame:
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::end_sweep_event()
{
  switch (current_state())
  {
  case state_ingame:
    break;
  
  default:
    assert(false);
    break;
  }
}

void core::fade_complete_event()
{
  switch (current_state())
  {
  case state_from_initial_to_credits:
    switch_state(state_credits);
    wait_time = 5.0;
    break;
  
  case state_credits:
    break;
  
  case state_from_credits_to_start_menu:
    _gui->hide_credits_screen();
    _gui->show_start_menu();
    _gui->switch_camera(_gui->arena_camera());
    switch_state(state_start_menu);
    _gui->fade_in();
    break;
    
  //case state_from_credits_to_main_menu:
  //  _gui->hide_credits_screen();
    //_gui->show_main_menu();
  //  _gui->switch_camera(_gui->arena_camera());
  //  switch_state(state_from_main_menu_to_start_menu);
  //  _gui->fade_in();
  //  break;
  
  //case state_main_menu:
  //  break;
  
  //case state_from_main_menu_to_start_menu:
  //  _gui->hide_main_menu();
  //  _gui->show_start_menu();
  //  _gui->flash_in();
  //  switch_state(state_start_menu);
  //  break;
  
  //case state_from_main_menu_to_quit:
  //  request_quit();
  //  switch_state(state_quit);
  //  break;
  
  case state_start_menu:
    break;
  
  case state_from_start_menu_to_quit:
    request_quit();
    switch_state(state_quit);
    break;
    
  //case state_from_start_menu_to_main_menu:
  //  _gui->hide_start_menu();
  //  _gui->show_main_menu();
  //  _gui->flash_in();
  //  switch_state(state_main_menu);
  //  break;
  
  case state_from_start_menu_to_ingame:
    _game = boost::shared_ptr<game>(
      new game(
        (_gui->options_player_one_human_selected() ? player_type_human : player_type_computer),
        (_gui->options_player_two_human_selected() ? player_type_human : player_type_computer),
        _gui->options_game_ends_selected(),
        _gui->options_game_stones_selected()));
    _gui->attach_game(_game);
    _gui->hide_start_menu();
    _gui->show_scoreboard();
    _gui->stop_title_music();
    _gui->start_arena_sound();
    _gui->flash_in();
    switch_state(state_ingame);
    break;
  
  case state_ingame:
    break;
    
  case state_from_ingame_to_game_menu:
    //_gui->remove_message();
    _gui->hide_powermeter();
    _gui->show_game_menu();
    _gui->hide_scoreboard();
    _gui->hide_player_one_winner_screen();
    _gui->hide_player_two_winner_screen();
    _gui->flash_in();
    switch_state(state_game_menu);
    break;
  
  case state_game_menu:
    break;
  
  case state_from_game_menu_to_ingame:
    _gui->hide_game_menu();
    _gui->show_scoreboard();
    _gui->show_powermeter();
    _gui->flash_in();
    switch_state(state_ingame);
    break;
  
  case state_from_game_menu_to_start_menu:
    _gui->detach_game();
    _game.reset();
    _gui->switch_camera(_gui->arena_camera());
    _gui->hide_scoreboard();
    _gui->hide_game_menu();
    _gui->hide_powermeter();
	_gui->show_start_menu();
    _gui->stop_arena_sound();
    _gui->play_title_music();
    _gui->flash_in();
    switch_state(state_start_menu);
    break;
    
  default:
    assert(false);
    break;
  }
}

void core::wait_over_event()
{
  switch (current_state())
  {
  case state_credits:
    _gui->fade_out();
    switch_state(state_from_credits_to_start_menu);
    break;
  
  case state_ingame:
    if (_game->final())
    {
      _gui->flash_out();
      switch_state(state_from_ingame_to_game_menu);
    }
    break;
    
  default:
    assert(false);
    break;
  }
}

void core::exit_event()
{
  switch (current_state())
  {
  //case state_main_menu:
  //  _gui->fade_out();
  //  switch_state(state_from_main_menu_to_quit);
  //  break;
  
  case state_start_menu:
    _gui->fade_out();
   // switch_state(state_from_start_menu_to_main_menu); 
	switch_state(state_from_start_menu_to_quit);
    break;
  
  case state_game_menu:
    _gui->flash_out();
    switch_state(state_from_game_menu_to_start_menu);
    break;
  
  case state_credits:
    break;
  
  case state_options_menu:
    _gui->hide_options_menu();
    _gui->show_start_menu();
    switch_state(state_start_menu);
    break;
    
  default:
    assert(false);
    break;
  }
}

core::state core::current_state() const
{
  return _current_state;
}

void core::switch_state( core::state next_state )
{
  _current_state = next_state;
}

}
